package intopo.evogame.partnerselection.population;

import intopo.evogame.partnerselection.player.AbstractPlayer;
import intopo.evogame.partnerselection.player.PlayerFactory;
import intopo.evogame.partnerselection.chromosome.Chromosome;
import intopo.evogame.partnerselection.chromosome.ChromosomeGenerator;
import intopo.game.Game;
import intopo.game.Strategy;

import java.io.IOException;
import java.util.Collection;

/**
 * An abstract population is a collection of unordered players.  This class extends {@code PlayerCollection} and provides methods to write players statistics to the database.
 * @author pedro
 * @param <S>
 * @param <C>
 * @param <P>
 */
abstract public class AbstractPopulation<
		S extends Strategy<S>,
		C extends Chromosome<S>,
		P extends AbstractPlayer<S, C, P>>
{
	/**
	 *
	 * <p> Statistic data.
	 */
	final protected Statistics stats;

	protected AbstractPopulation ()
	{
		super ();
		this.stats = new Statistics ();
	}

//	protected AbstractPopulation (ChromosomeGenerator<S, C> generator, PlayerFactory<S, C, P> factory)
//	{
//		super (generator, factory);
//		int j;
//		this.stats = new Statistics ();
//
//		for (j = generator.size () - 1; j >= 0; j--) {
//			C chromosome = generator.next ();
//			if (chromosome.strategy.isCooperator ()) {
//				this.stats.cooperators++;
//			}
//			this.players.add (factory.create (chromosome));
//			this.stats.utilityThreshold += chromosome.utilityThreshold;
//		}
//		this.stats.players += this.players.size ();
////		System.out.println ("Number of players: " + this.players.size ());
//	}

	/**
	 * Calculates the next generation of players. Players that have reached the reproduction phase are replaced by their offspring.
	 */
	abstract public boolean nextGeneration (int time)
		throws IOException;

	/**
	 * Print a summary of this population from round one:
	 * <ul>
	 * <li>How many players have been in the population</li>
	 * <li>How many cooperative players</li>
	 * <li>The sum of field {@code Chromosome.poolSize} that represents the gene that controls the size of probability and combination vectors</li>
	 * </ul>
	 */
	void printSummary ()
	{
//		this.summaryStream.print (this.stats.players);
//		this.summaryStream.print ('\t');
//		this.summaryStream.print (this.stats.cooperators);
//		this.summaryStream.print ('\t');
//		this.summaryStream.print (this.stats.poolSize);
//		this.summaryStream.print ('\t');
//		this.summaryStream.print (this.stats.delta);
//		this.summaryStream.print ('\t');
//		this.summaryStream.print (this.stats.utilityThreshold);
//		this.summaryStream.println ();
	}
//	final protected void writePhenotypes (int time)
//		throws IOException
//	{
//		for (P p : players) {
//			p.writePhenotype (time);
//		}
//	}

//	final protected void writeChromosomes (Collection<P> pl)
//		throws IOException
//	{
//		for (P p : pl) {
//			Chromosome c = p.getChromosome ();
//			c.writeStream ();
//		}
//	}
	/**
	 * Generic population statistic
	 * @param <S>
	 * @param <C>
	 * @param <P>
	 */
	static class Statistics<
		S extends Strategy<S>,
		C extends Chromosome<S>,
		P extends AbstractPlayer<S, C, P>>
	{
		/**
		 * Sum of gene pool size of all players in a round.
		 */
		private long poolSize;
		/**
		 * Sum of gene delta of all players in a round.
		 */
		private double delta;
		/**
		 * Sum of gene utility threshold of all players in a round.
		 */
		private double utilityThreshold;
		/**
		 * Number of players in a round.
		 */
		private long players;
		/**
		 * Number of cooperators in a round.
		 *
		 * <p> Statistic data.
		 */
		private long cooperators;
		/**
		 * Construct an initialised statistical data.
		 */
		Statistics ()
		{
			init ();
		}
		/**
		 * Initialise statistical data.  Zero all data.
		 */
		protected void init ()
		{
			this.poolSize = 0;
			this.delta = 0.0;
			this.utilityThreshold = 0.0;
			this.players = 0;
			this.cooperators = 0;
		}
		protected void process (P player)
		{
			C c = player.getChromosome ();
			this.poolSize += c.poolSize;
			this.delta += c.delta;
			this.utilityThreshold += c.utilityThreshold;
			this.players++;
			if (c.strategy.isCooperator ()) {
				this.cooperators++;
			}
		}
	}
}

// Local Variables: 
// mode: flyspell-prog
// ispell-local-dictionary: "british"
// End:
